package com.zonerobotics.tutorial.btcontrol;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.util.Timer;
import java.util.TimerTask;

import com.zonerobotics.tutorial.btcontrol.R;
import com.zonerobotics.zBotAPI.Enums.*;
import com.zonerobotics.zBotAPI.Listeners.IBotListener;
import com.zonerobotics.zBotAPI.ZBot.*;
import com.zonerobotics.zBotAPI.ZMsg.ZImage;
import com.zonerobotics.zBotAPI.ZMsg.ZJoy;
import com.zonerobotics.zBotAPI.Listeners.*;

import android.R.string;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.text.Layout;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

public class ControlbotActivity extends Activity implements IBotListener{

	// Debugging
	private static final String TAG = "ControlBotAct";
	private static final boolean D = true;
	public TextView logWindow;

	// device's mac address
	String devAddr;

	// gui parts
	public static TextView logView;
	public static TextView tvDirection;
	public static TextView tvTopStatus;
	public static Layout layout;
	// public static VideoView vvCameraView;
	public static SeekBar sbSensitivity;
	// battery
	public static TextView tvBattPercent;
	public static ProgressBar pbBattPercent;


	public static ImageView imgViewLastImg;

	// for handling
	public static final int CONNECTION_MADE = 1;
	public static final int CONNECTION_FAILED = 2;
	public static final int CONNECTION_LOST = 3;
	public static final int MESSAGE_MSG = 4;
	public static final int UPDATE_GUI = 5;
	public static final int UPDATE_GUI_STATS = 6;
	public static final int NEWIMAGE_A = 7;
	public static final int NEWIMAGE_B = 8;


	// for updating gui etc
	public static Bitmap lastImg = null;
	Timer updateTimer = new Timer("TimerUpdate");
	
	// this is our actual bot
	public IZBot myBot = null;
	// these are for the joystick tracking
    // create empty axes (x,y,z) z is camera
    float[] axes = {0.0f,0.0f,0.0f};
    // create empty buttons
    int[] buttons = {0,0,0,0,0,0,0,0};
	// these are just for talking about the frame
	static int frames = 0;

	static double[] timeStamps = {System.nanoTime(),System.nanoTime(),System.nanoTime()};
	static int imgOffset = 3;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_controlbot);
		Intent i = getIntent();
		devAddr = (String) i.getExtras().getString("devAddr");
		initGui();
		// // setup the sensor
		// mSensorManager = (SensorManager)
		// getSystemService(Context.SENSOR_SERVICE);
		// mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		// mSensorManager.registerListener(this,
		// mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
		// mSensorManager.SENSOR_DELAY_NORMAL);

	}

	public static double Round(double value, int places) {
		if (places < 0)
			throw new IllegalArgumentException();

		long factor = (long) Math.pow(10, places);
		value = value * factor;
		long tmp = Math.round(value);
		return (double) tmp / factor;
	}

	private void initGui() {
		logView = (TextView) findViewById(R.id.textViewLog);
		layout = logView.getLayout();
		tvBattPercent = (TextView) findViewById(R.id.textViewBattPercent);
		pbBattPercent = (ProgressBar) findViewById(R.id.progressBarBat);
		tvDirection = (TextView) findViewById(R.id.textViewDirection);
		tvTopStatus = (TextView) findViewById(R.id.textViewTopStatus);

		// initial push down of movement/motor buttons
		((Button) findViewById(R.id.button1)).setOnTouchListener(onTouchListener);
		((Button) findViewById(R.id.button2)).setOnTouchListener(onTouchListener);
		((Button) findViewById(R.id.button3)).setOnTouchListener(onTouchListener);
		((Button) findViewById(R.id.button4)).setOnTouchListener(onTouchListener);
		((Button) findViewById(R.id.button5)).setOnTouchListener(onTouchListener);
		((Button) findViewById(R.id.button6)).setOnTouchListener(onTouchListener);
		
		// release of movement motor buttons
		((Button) findViewById(R.id.button1)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.button2)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.button3)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.button4)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.button5)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.button6)).setOnClickListener(onClickListener);
		
		// radio buttons
		((Button) findViewById(R.id.RadioButton00)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.RadioButton01)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.RadioButton02)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.RadioButton03)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.RadioButton04)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.RadioButton05)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.RadioButton06)).setOnClickListener(onClickListener);
		((Button) findViewById(R.id.RadioButton07)).setOnClickListener(onClickListener);
		
		imgViewLastImg = (ImageView) findViewById(R.id.imageView1);
		
		String strStats = "TX: 0 RX: 0";
		((TextView)findViewById(R.id.textViewTopStatus)).setText(strStats);
		


	}

	// The Handler that gets information back from the BluetoothChatService
	private final Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			// turn off the spinner, we are done waiting
			//progBarInitialWait.setVisibility(View.GONE);
			DecimalFormat df = new DecimalFormat("#.##");
			switch (msg.what) {
			case MESSAGE_MSG:
				logView.append("\n" + (String) msg.obj);
				break;

			case CONNECTION_FAILED:
				logView.append("\nConnection Failed!");
				break;
			case CONNECTION_MADE:
				logView.append("\nConnection Succeeded!");
				break;
			case UPDATE_GUI:
				// put new bmp
				if (lastImg != null) {
					imgViewLastImg.setImageBitmap(lastImg);
					lastImg = null;
					double timeDelta = System.nanoTime() - timeStamps[0];
					tvDirection.setText("Frames: " + ++frames
							+ " Time Delta MS: " + df.format(timeDelta/1000000));
					timeStamps[0] =  System.nanoTime();
					timeStamps[1] =  System.nanoTime() - timeStamps[1];
				}
				break;
			case UPDATE_GUI_STATS:
				if(myBot == null)
				{
					break;
				}
				
				ZBotStats zbs = myBot.getStats();
				String strStats = "TXKB: " + zbs.getTXBytes()/1000
						+ " RXKB: " + zbs.getRXBytes()/1000
						+ " TXKbps: " + df.format(zbs.getTXbps()/1024)
						+ " RXKbps: " + df.format(zbs.getRXbps()/1024);
				((TextView)findViewById(R.id.textViewTopStatus)).setText(strStats);
				break;
				
			case NEWIMAGE_A:
				myBot.getImageA();
				if(lastImg == null)
				{
					lastImg = Bitmap.createBitmap(myBot.getImageA().getWidth(), 
							myBot.getImageA().getHeight(), Bitmap.Config.RGB_565);
				}

				ByteBuffer bb = null;
				bb = ByteBuffer.wrap(myBot.getImageA().getData());

				lastImg.copyPixelsFromBuffer(bb);
				// put new bmp
				if (lastImg != null) {
					imgViewLastImg.setImageBitmap(lastImg);
					lastImg = null;
					double timeDelta = System.nanoTime() - timeStamps[0];
					tvDirection.setText("Frames: " + ++frames
							+ " Time Delta MS: " + df.format(timeDelta/1000000));
					timeStamps[0] =  System.nanoTime();
					timeStamps[1] =  System.nanoTime() - timeStamps[1];
				}
				break;
				
			case NEWIMAGE_B:
				myBot.getImageB();
				if(lastImg == null)
				{
					lastImg = Bitmap.createBitmap(myBot.getImageB().getWidth(), 
							myBot.getImageB().getHeight(), Bitmap.Config.RGB_565);
				}

				ByteBuffer bbB = null;
				bbB = ByteBuffer.wrap(myBot.getImageB().getData());

				lastImg.copyPixelsFromBuffer(bbB);
				// put new bmp
				if (lastImg != null) {
					imgViewLastImg.setImageBitmap(lastImg);
					lastImg = null;
					double timeDelta = System.nanoTime() - timeStamps[0];
					tvDirection.setText("Frames: " + ++frames
							+ " Time Delta MS: " + df.format(timeDelta/1000000));
					timeStamps[0] =  System.nanoTime();
					timeStamps[1] =  System.nanoTime() - timeStamps[1];
				}
				break;
				
			}
		}
	};
	
	
	// we are using async so the timer was nice but taking it out 

	private void startTimer() {
		updateTimer.scheduleAtFixedRate(new TimerTask() {
			public void run() {
				mHandler.obtainMessage(ControlbotActivity.UPDATE_GUI_STATS)
						.sendToTarget();
			}
		}, (long) 0, (long) 100);
	}

	public void updateGuiFromZPacket() {
		// if (logView.getText().length() > 2048) {
		// logView.setText("");
		// logView.append("\nLog Cleared");
		// }
		//
		// if (myBot == null) {
		// return;
		// }
		// // todo set intervals for when to update things !
		//
		// // update this every 5 sec
		// if ((guiUpdateDelta % 50) == 0) {
		// int battPercent = Math.round(((float)
		// myBot.getValue(EzReadVars.ADCIN1) / 4096) * 100);
		// pbBattPercent.setProgress(battPercent);
		// tvBattPercent.setText(Integer.toString(battPercent) + "%");
		// }
		//
		// guiUpdateDelta++;
		// if (guiUpdateDelta == 100) {
		// guiUpdateDelta = 0;
		// long rxTime = System.currentTimeMillis() - lastRXCalc;
		// float Bps = ((float) myPoller.bytesRX * 100 / rxTime) * 100;
		// float bps = Bps * 8;
		// logView.append("\nRX Bps:" + Bps + " bps:" + bps);
		// lastRXCalc = System.currentTimeMillis();
		// myPoller.bytesRX = 0;
		// }
		//
		// // update image
		// // todo ?
		//
		// // TODO this sort of thing should not be read back but should
		// // be verified after written. If we sent the packet to turn on
		// // the light for instance we will never read that back from the
		// // device, looking at a static var is dumb. We should set these
		// // when the button is hit, then assume or verify they are written!
		// int GPIO = 0;
		// StringBuilder StatGpio = new StringBuilder();
		// // update top status
		// if ((GPIO & GPIO_LIGHT) != 0)
		// StatGpio.append("(LIG)");
		// if ((GPIO & GPIO_FIRE) != 0)
		// StatGpio.append("(FIR)");
		// if ((GPIO & GPIO_FRIEND) != 0)
		// StatGpio.append("(FRI)");
		// if ((GPIO & GPIO_AUX1) != 0)
		// StatGpio.append("(AX1)");
		// tvTopStatus.setText(StatGpio.toString());
		//
		// // this is the same as above, its pointless to read some var we wrote
		// //ByteBuffer bb =
		// ByteBuffer.allocate(4).putInt(myBot.getValue(EzReadVars.p));
		// //byte[] duties = bb.array();
		// //tvDirection.setText("Left Motor ["+duties[0]+":"+duties[1]+"] Right Motor["+duties[2]+":"+duties[3]+"]");
	}

	View.OnTouchListener onTouchListener = new View.OnTouchListener() {
		
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			View b = v;
			int oldGPIO = 0, newGpio = 0;

			if (myBot == null) {
				return true;
			}

			switch (b.getId()) {
			
			case R.id.button1: // cam up
				axes[2] = 1;
				break;
			case R.id.button2: // GO
				axes[0] = 1; // x axis forward 
				break;
			case R.id.button3: // left 
				axes[1] = -1; // y axes -1
				break;
			case R.id.button4: // right
				axes[1] = 1;
				break;
			case R.id.button5: // back
				axes[0] = -1; // x axis
				break;
			case R.id.button6: // cam down
				axes[2] = -1; // z axes ? 
				break;
			}
			
			ZJoy joy = new ZJoy();
			joy.setAxes(axes);
			joy.setButtons(buttons);
			myBot.onJoyChange(joy);
			
			return false; // let onclick be handled when they release
		}
	};
	
	/***
	 * On click actually gets called after you have already pressed the
	 * button, so its the same as motionevent.actionup. This is where we 
	 * toggle gpio or release / stop the motors
	 */
	View.OnClickListener onClickListener = new View.OnClickListener() {
		// handle all button presses
		public void onClick(View v) {
			// Do something in response to button click
			View b = v;
			int oldGPIO = 0, newGpio = 0;

			if (myBot == null) {
				return;
			}

			switch (b.getId()) {
			// onclick is called when they release the button stop the motors
			case R.id.button1: // cam up
			case R.id.button2: // GO
			case R.id.button3: // left 
			case R.id.button4: // right
			case R.id.button5: // back
			case R.id.button6: // cam down
				axes[0] = axes[1] = axes[2] = 0; // z axes ? 
				break;
				
			// now all the radio buttons are gpio, toggle them
			case R.id.RadioButton00: // right most
				// TODO I WAS HERE !!
				buttons[7] = buttons[7] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton00)).setChecked(buttons[7] == 1);
				break;
			case R.id.RadioButton01:
				buttons[6] = buttons[6] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton01)).setChecked(buttons[6] == 1);
				break;
			case R.id.RadioButton02:
				buttons[5] = buttons[5] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton02)).setChecked(buttons[5] == 1);
				break;
			case R.id.RadioButton03:
				buttons[4] = buttons[4] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton03)).setChecked(buttons[4] == 1);
				break;
			case R.id.RadioButton04:
				buttons[3] = buttons[3] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton04)).setChecked(buttons[3] == 1);
				break;
			case R.id.RadioButton05:
				buttons[2] = buttons[2] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton05)).setChecked(buttons[2] == 1);
				break;
			case R.id.RadioButton06:
				buttons[1] = buttons[1] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton06)).setChecked(buttons[1] == 1);
				break;
			case R.id.RadioButton07:
				buttons[0] = buttons[0] == 1 ? 0 : 1;
				((RadioButton)findViewById(R.id.RadioButton07)).setChecked(buttons[0] == 1);
				break;
			
			}
			ZJoy joy = new ZJoy();
			joy.setAxes(axes);
			joy.setButtons(buttons);
			myBot.onJoyChange(joy);
		}
	};

	@Override
	public void onStart() {
		super.onStart();
		if (D)
			Log.e(TAG, "++ ON START ++");
		// if we have never setup our polling service we
		// need to do that now
		if (myBot == null) {
			// init the service and bot using example
			InitBTComm init = new InitBTComm(this.devAddr, this);
			myBot = init.zBot;
			
			// start gui update timer
			startTimer();

		}

		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//		if (updateTimer == null) {
//			startTimer();
//		}
	}

	@Override
	public synchronized void onResume() {
		super.onResume();
		if (D)
			Log.e(TAG, "- ON RESUME -");
	}

	@Override
	public synchronized void onPause() {
		super.onPause();
		if (D)
			Log.e(TAG, "- ON PAUSE -");
		// if the user hits back, close all sockets kill this !
		// either they are watching it on the screen or they are starting over !
		getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		if (myBot != null) {
			// epic fail
			myBot.killBot();
		}
		finish();
	}

	@Override
	public void onStop() {
		super.onStop();
		if (D)
			Log.e(TAG, "-- ON STOP --");
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		if (D)
			Log.e(TAG, "--- ON DESTROY ---");
	}

	@Override
	public void onNewImage(ZImage image) {
		
		timeStamps[1] = System.currentTimeMillis();
		if(lastImg == null)
		{
			lastImg = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.RGB_565);
		}

		ByteBuffer bb = null;
		bb = ByteBuffer.wrap(image.getData());

		lastImg.copyPixelsFromBuffer(bb);

		Message msg = new Message();
		msg.what = UPDATE_GUI;
		this.mHandler.sendMessage(msg);

	}

	@Override
	public void onNewEvent(EzEvent event) {

		Message msg = new Message();
		switch (event) {
		case connected:
			msg.what = MESSAGE_MSG;
			msg.obj = "New Event: " + (String) event.name();
			this.mHandler.sendMessage(msg);
			break;
		case disconnected:
			msg.what = MESSAGE_MSG;
			msg.obj = "New Event: " + (String) event.name();
			this.mHandler.sendMessage(msg);
			//logView.append("\nNew Event: " + (String) event.name());
			break;
		case imageA:
			msg.what = NEWIMAGE_A;
			msg.obj = "New Event: " + (String) event.name();
			this.mHandler.sendMessage(msg);
			break;
		case imageB:
			msg.what = NEWIMAGE_B;
			msg.obj = "New Event: " + (String) event.name();
			this.mHandler.sendMessage(msg);
			break;
		}
	}

	@Override
	public void onLogMessage(String message) {
		// TODO Auto-generated method stub
		if (D)
		{
			Log.e(TAG, ":" + message);
			Message msg = new Message();
			msg.what = MESSAGE_MSG;
			msg.obj = (String) message;
			this.mHandler.sendMessage(msg);
		}

	}
}
